package com.easyCode.sqlMapper.template;

import com.easyCode.sqlMapper.mapInfo.DataBaseMapInfo;
import com.easyCode.sqlMapper.mapInfo.TableMapInfo;
import com.easyCode.sqlMapper.renderHandler.*;
import com.easyCode.util.FileUtil;
import com.easyCode.util.JavaUtil;
import com.easyCode.util.StringUtil;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
import org.apache.velocity.runtime.resource.loader.FileResourceLoader;
import org.apache.velocity.runtime.resource.loader.JarResourceLoader;

import java.io.FileNotFoundException;
import java.io.PrintStream;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;

import static com.easyCode.sqlMapper.template.SqlTemplateConfig.DEFAULT_TEMPLATE_BASE_DIR;

/**
 * Velocity模板引擎
 *
 * @author 大忽悠
 * @create 2022/12/2 15:04
 */
public class VelocitySqlTemplate extends SqlTemplate {
    private final VelocityEngine velocityEngine;
    private final Map<String,RenderHandler> renderHandlerMap=new HashMap<>();

    public VelocitySqlTemplate(SqlTemplateConfig sqlTemplateConfig) {
        super(sqlTemplateConfig);
        velocityEngine = initEngine();
        if(sqlTemplateConfig.getDefaultConfig()){
            registerDefaultTemplates();
        }
    }

    /**
     * 注册默认提供的模板文件
     */
    private void registerDefaultTemplates() {
        renderHandlerMap.put(sqlTemplateConfig.getDefaultTemplateFilePath(DefaultTemplateManager.DAO_TEMPLATE),new DaoRenderHandler());
        renderHandlerMap.put(sqlTemplateConfig.getDefaultTemplateFilePath(DefaultTemplateManager.DTO_TEMPLATE),new DtoRenderHandler());
        renderHandlerMap.put(sqlTemplateConfig.getDefaultTemplateFilePath(DefaultTemplateManager.VO_TEMPLATE),new VoRenderHandler());
        renderHandlerMap.put(sqlTemplateConfig.getDefaultTemplateFilePath(DefaultTemplateManager.FACTORY_TEMPLATE),new FactoryRenderHandler());
        renderHandlerMap.put(sqlTemplateConfig.getDefaultTemplateFilePath(DefaultTemplateManager.SERVICE_TEMPLATE),new ServiceRenderHandler());
        renderHandlerMap.put(sqlTemplateConfig.getDefaultTemplateFilePath(DefaultTemplateManager.SERVICE_IMPL_TEMPLATE),new ServiceImplRenderHandler());
        renderHandlerMap.put(sqlTemplateConfig.getDefaultTemplateFilePath(DefaultTemplateManager.MAPPER_TEMPLATE),new MapperRenderHandler());
        renderHandlerMap.put(sqlTemplateConfig.getDefaultTemplateFilePath(DefaultTemplateManager.CONTROLLER_TEMPLATE),new ControllerRenderHandler());
    }

    /**
     * 设置模板文件的寻找路径,默认为类路径下
     */
    private VelocityEngine initEngine() {
        VelocityEngine velocityEngine;
        velocityEngine = new VelocityEngine();
        velocityEngine.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
        velocityEngine.setProperty("classpath.resource.loader.class",CustomClassPathResourceLoader.class.getName());
        velocityEngine.init();
        return velocityEngine;
    }

    /**
     * 渲染三部曲: <br>
     * 1.准备参数 <br>
     * 2.渲染 <br>
     * 3.输出<br>
     */
    @Override
    public void render(DataBaseMapInfo dataBaseMapInfo){
        //依次对每个表进行渲染处理
        for (TableMapInfo tableMapInfo : dataBaseMapInfo.getTableMapInfos()) {
            renderHandlerMap.forEach((templateFilePath,renderHandler)->{
                try {
                    doRender(templateFilePath,tableMapInfo,renderHandler);
                } catch (FileNotFoundException e) {
                    throw new RuntimeException(e);
                }
            });
        }
    }

    @Override
    public void addTemplate(String templateFileName, RenderHandler renderHandler) {
         renderHandlerMap.put(sqlTemplateConfig.getTemplateFilePath(templateFileName),renderHandler);
    }

    @Override
    public void removeTemplate(String templateFileName) {
        renderHandlerMap.remove(sqlTemplateConfig.getTemplateFilePath(templateFileName));
    }

    @Override
    public void addDefaultTemplate(String templateFileName, RenderHandler renderHandler) {
        renderHandlerMap.put(sqlTemplateConfig.getDefaultTemplateFilePath(templateFileName),renderHandler);
    }

    @Override
    public void removeDefaultTemplate(String templateFileName) {
        renderHandlerMap.remove(sqlTemplateConfig.getDefaultTemplateFilePath(templateFileName));
    }


    /**
     * 模板方法模式应用
     */
    private void doRender(String templateFilePath, TableMapInfo tableMapInfo, RenderHandler renderHandler) throws FileNotFoundException {
        //1.获取模板文件
        Template template = velocityEngine.getTemplate(templateFilePath);
        //2.准备参数
        VelocityContext velocityContext = new VelocityContext();
        TemplateContext templateContext=TemplateContext.getContext();
        String outputJavaFilePath = sqlTemplateConfig.getModuleActualPath(renderHandler.paramPrepare(velocityContext, tableMapInfo,sqlTemplateConfig,templateContext));
        FileUtil.createDirIfNotExist(outputJavaFilePath);
        //3.渲染
        StringWriter stringWriter = new StringWriter();
        template.merge(velocityContext, stringWriter);
        //4.输出
        String javFileName = getJavaFileName(tableMapInfo, templateContext);
        PrintStream printStream = new PrintStream(StringUtil.pathMerge(true,outputJavaFilePath,javFileName));
        printStream.print(JavaUtil.formJava(stringWriter.toString()));
    }

    private String getJavaFileName(TableMapInfo tableMapInfo, TemplateContext templateContext) {
        String javFileName = templateContext.getVal(TemplateContext.JAVA_FILE_NAME);
        javFileName=StringUtil.isNullOrEmpty(javFileName)? tableMapInfo.getJavaFileName():javFileName;
        return javFileName;
    }
}
